﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;

namespace NetworkingLibrary.Clients.Specialised
{
    public partial class ManagerServerClient : TCP.ITCPClient
    {
        public delegate void GetHostAddressesCallback(Dictionary<IPAddress, IPAddress> Addresses);
        public delegate void AddServerCallback(bool Added);
        public delegate void RemoveServerCallback(bool Removed);

        TCP.TCPClient TheTCPClient = null;

        public int MaxFlowRate
        {
            get
            {
                return TheTCPClient.MaxFlowRate;
            }
            set
            {
                TheTCPClient.MaxFlowRate = value;
            }
        }
        public DateTime LastSendTime
        {
            get
            {
                return TheTCPClient.LastSendTime;
            }
            set
            {
                TheTCPClient.LastSendTime = value;
            }
        }
        public bool FlowControl
        {
            get
            {
                return TheTCPClient.FlowControl;
            }
            set
            {
                TheTCPClient.FlowControl = value;
            }
        }
        
        public ManagerServerClient(System.Net.IPAddress ManagerServerAddress, int ManagerServerPort, bool DoConnectAndStart = true) 
            : base()
        {
            TheTCPClient = new TCP.TCPClient();
            InitImplementation();

            if (DoConnectAndStart)
            {
                if (!TheTCPClient.Connect(ManagerServerPort, ManagerServerAddress, true))
                {
                    throw new Exceptions.ClientConnectFailException();
                }
            }
        }

        private GetHostAddressesCallback TheGetHostAddressesCallback = null;
        private AddServerCallback TheAddServerCallback = null;
        private RemoveServerCallback TheRemoveServerCallback = null;

        public Dictionary<System.Net.IPAddress, System.Net.IPAddress> GetHostAddresses(bool Async = false, GetHostAddressesCallback Callback = null)
        {
            try
            {
                if (!Async)
                {
                    Messages.Specialised.Manager.Responses.GameServersResponseMessage WantedResponse = (Messages.Specialised.Manager.Responses.GameServersResponseMessage)TheTCPClient.GetMessageResponse(new Messages.Specialised.Manager.Requests.RequestGameServersMessage(), typeof(Messages.Specialised.Manager.Responses.GameServersResponseMessage));
                    if (WantedResponse != null)
                    {
                        return WantedResponse.HostAddresses;
                    }
                    else
                    {
                        throw new NullReferenceException("WantedResponse");
                    }
                }
                else
                {
                    TheGetHostAddressesCallback = Callback;
                    TheTCPClient.Send(new Messages.Specialised.Manager.Requests.RequestGameServersMessage());
                    return null;
                }
            }
            catch(Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return new Dictionary<System.Net.IPAddress, System.Net.IPAddress>();
        }
        public bool AddServer(System.Net.IPAddress ServerAddressTCP, System.Net.IPAddress ServerAddressUDP, bool Async = false, AddServerCallback Callback = null)
        {
            try
            {
                if (!Async)
                {
                    Messages.Specialised.Manager.Responses.AddGameServerResponseMessage WantedResponse = (Messages.Specialised.Manager.Responses.AddGameServerResponseMessage)TheTCPClient.GetMessageResponse(new Messages.Specialised.Manager.Requests.AddGameServerRequestMessage(ServerAddressTCP, ServerAddressUDP), typeof(Messages.Specialised.Manager.Responses.AddGameServerResponseMessage));
                    if (WantedResponse != null)
                    {
                        return WantedResponse.Added;
                    }
                    else
                    {
                        throw new NullReferenceException("WantedResponse");
                    }
                }
                else
                {
                    TheAddServerCallback = Callback;
                    TheTCPClient.Send(new Messages.Specialised.Manager.Requests.AddGameServerRequestMessage(ServerAddressTCP, ServerAddressUDP));
                    return false;
                }
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }
        public bool RemoveServer(System.Net.IPAddress ServerAddressTCP, bool Async = false, RemoveServerCallback Callback = null)
        {
            try
            {
                if (!Async)
                {
                    Messages.Specialised.Manager.Responses.RemoveGameServerResponseMessage WantedResponse = (Messages.Specialised.Manager.Responses.RemoveGameServerResponseMessage)TheTCPClient.GetMessageResponse(new Messages.Specialised.Manager.Requests.RemoveGameServerRequestMessage(ServerAddressTCP), typeof(Messages.Specialised.Manager.Responses.RemoveGameServerResponseMessage));
                    if (WantedResponse != null)
                    {
                        return WantedResponse.Removed;
                    }
                    else
                    {
                        throw new NullReferenceException("WantedResponse");
                    }
                }
                else
                {
                    TheRemoveServerCallback = Callback;
                    TheTCPClient.Send(new Messages.Specialised.Manager.Requests.RemoveGameServerRequestMessage(ServerAddressTCP));
                    return false;
                }
            }
            catch (Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
            return false;
        }

        private void TheTCPClient_OnMessageReceived(Events.MessageReceivedEventArgs e)
        {
            try
            {
                OnMessageReceived.Invoke(e);
                if(!e.PreventDefault)
                {
                    Type MessageType = e.TheMessage.GetType();
                    if (MessageType == typeof(Messages.Specialised.Manager.Responses.GameServersResponseMessage))
                    {
                        if (TheGetHostAddressesCallback != null)
                        {
                            TheGetHostAddressesCallback.Invoke(((Messages.Specialised.Manager.Responses.GameServersResponseMessage)e.TheMessage).HostAddresses);
                        }
                    }
                    else if (MessageType == typeof(Messages.Specialised.Manager.Responses.AddGameServerResponseMessage))
                    {
                        if (TheAddServerCallback != null)
                        {
                            TheAddServerCallback.Invoke(((Messages.Specialised.Manager.Responses.AddGameServerResponseMessage)e.TheMessage).Added);
                        }
                    }
                    else if (MessageType == typeof(Messages.Specialised.Manager.Responses.RemoveGameServerResponseMessage))
                    {
                        if (TheRemoveServerCallback != null)
                        {
                            TheRemoveServerCallback.Invoke(((Messages.Specialised.Manager.Responses.RemoveGameServerResponseMessage)e.TheMessage).Removed);
                        }
                    } 
                }
            }
            catch(Exception ex)
            {
                OnError.Invoke(new Events.ErrorEventArgs(ex));
            }
        }

        #region ITCPClient Implementation

        public event Events.ClientConnectDelegate OnConnect = delegate { };
        public event Events.ClientDisconnectDelegate OnDisconnect = delegate { };
        public event Events.MessageReceivedDelegate OnMessageReceived = delegate { };
        public event Events.ErrorDelegate OnError = delegate { };

        public Guid ID
        {
            get
            {
                return TheTCPClient.ID;
            }
            set
            {
                TheTCPClient.ID = value;
            }
        }
        public int BufferSize
        {
            get;
            set;
        }
        public bool Terminating
        {
            get;
            set;
        }
        public bool Connected
        {
            get
            {
                return TheTCPClient.Connected;
            }
        }

        public IPAddress LocalAddress
        {
            get
            {
                return TheTCPClient.LocalAddress;
            }
        }
        public IPAddress RemoteAddress
        {
            get
            {
                return TheTCPClient.RemoteAddress;
            }
        }

        public bool Connect(int Port, IPAddress TheAddress, bool ShouldStart = true)
        {
            return TheTCPClient.Connect(Port, TheAddress, ShouldStart);
        }
        public bool Disconnect(bool IgnoreSocketState = false)
        {
            return TheTCPClient.Disconnect(IgnoreSocketState);
        }
        
        public bool Start()
        {
            return TheTCPClient.Start();
        }
        public bool Stop()
        {
            return TheTCPClient.Stop();
        }

        public bool Send(byte[] Data)
        {
            return TheTCPClient.Send(Data);
        }
        public bool Send(Messages.MessageObject TheMessage)
        {
            return TheTCPClient.Send(TheMessage);
        }


        private void InitImplementation()
        {
            TheTCPClient.OnConnect += new Events.ClientConnectDelegate(TheTCPClient_OnConnect);
            TheTCPClient.OnDisconnect += new Events.ClientDisconnectDelegate(TheTCPClient_OnDisconnect);
            TheTCPClient.OnError += new Events.ErrorDelegate(TheTCPClient_OnError);
            TheTCPClient.OnMessageReceived += new Events.MessageReceivedDelegate(TheTCPClient_OnMessageReceived);
        }

        private void TheTCPClient_OnConnect(Events.ClientConnectEventArgs e)
        {
            OnConnect.Invoke(e);
        }
        private void TheTCPClient_OnDisconnect(Events.ClientDisconnectEventArgs e)
        {
            OnDisconnect.Invoke(e);
        }
        private void TheTCPClient_OnError(Events.ErrorEventArgs e)
        {
            OnError.Invoke(e);
        }

        public Messages.MessageObject GetMessageResponse(Messages.MessageObject MessageToSend, Type WantedResponseType)
        {
            return TheTCPClient.GetMessageResponse(MessageToSend, WantedResponseType);
        }

        #endregion
    }
}
